#include "tetrixserver.h"

std::auto_ptr<TetrixServer> TetrixServer::_instance;

std::auto_ptr<TetrixServer>& TetrixServer::getInstance()
{
    if(_instance.get() == 0)
        _instance.reset(new TetrixServer);

    return _instance;
}

TetrixServer::~TetrixServer()
{
    _master_set.reset();
    _active_set.reset();
    _acceptor.close();

    ACE_DEBUG((LM_INFO,"I am Leaving\n"));
}

bool TetrixServer::run(int argc,char *argv[])
{
    if(argc < 2){
        ACE_ERROR((LM_ERROR,"Usage : TetrixServer Port\n"));
        return false;
    }

    if(!open(atoi(argv[1]))){
	std::string error(strerror(errno));
        ACE::write_n(ACE_STDOUT,error.data(),error.length());
        return false;
    }

    ACE_DEBUG((LM_INFO,"I am Listening in %d Port\n",atoi(argv[1])));

    while(!_stop){
	if(wait_for_multi_events()){
	    handle_connections();
	    handle_data();
	}
    }

    return true;
}

void TetrixServer::remove(ACE_SOCK_Stream& peer)
{
    ACE_INET_Addr addr;
    peer.get_remote_addr(addr);
    ACE_DEBUG((LM_DEBUG,"Remove Client(%s:%d)\n\n",addr.get_host_addr(),addr.get_port_number()));

    _master_set.clr_bit(peer.get_handle());
    _master_set.sync(_master_set.max_set() + 1);
    RoomManager::getInstance()->removeClient(peer.get_handle());

    peer.close();
}

bool TetrixServer::open(quint16 port)
{
    ACE::set_handle_limit();

    ACE_INET_Addr addr;
    if(addr.set(port) == -1 || _acceptor.open(addr,1) == -1)
        return false;

    _acceptor.enable(ACE_NONBLOCK);
    _master_set.set_bit(_acceptor.get_handle());
    return true;
}

bool TetrixServer::wait_for_multi_events()
{
    _active_set = _master_set;
    ACE_Time_Value timeout;
    timeout.sec(2);
    timeout.usec(0);

    if(ACE::select(_active_set.max_set() + 1,_active_set,&timeout) <= 0){
        return false;
    }

    _active_set.sync(_active_set.max_set() + 1);
    return true;
}

bool TetrixServer::handle_connections()
{
    if(_active_set.is_set(_acceptor.get_handle())){
	ACE_DEBUG((LM_DEBUG,"Handle Connection\n"));

	ACE_SOCK_Stream client;
        ACE_INET_Addr client_addr;
	while(_acceptor.accept(client) == 0){  //accept client successfully
	    client.get_remote_addr(client_addr);
            ACE_DEBUG((LM_INFO,"Got Client(IP:%s,Port:%d)\n\n",client_addr.get_host_addr(),client_addr.get_port_number()));

	    client.enable(ACE_NONBLOCK);
	    _master_set.set_bit(client.get_handle());
        }

        _active_set.clr_bit(_acceptor.get_handle());
    }

    _active_set.sync(_active_set.max_set() + 1);
    return true;
}

bool TetrixServer::handle_data()
{
    ACE_Handle_Set_Iterator iter(_active_set);
    ACE_HANDLE handle;
    while((handle = iter()) != ACE_INVALID_HANDLE)
    {
	ACE_SOCK_Stream client(handle);
	ACE_INET_Addr addr;
	client.get_remote_addr(addr);
	ACE_DEBUG((LM_DEBUG,"Data from Client(%s:%d)\n",addr.get_host_addr(),addr.get_port_number()));

	std::auto_ptr<ProtoSrv> request(ProtoSrvManager::getInstance()->decode(client));
	if(request.get() == 0){
	    remove(client);
	}
	else{
            ACE_DEBUG((LM_DEBUG,"Got Request(%d:%s)\n",request->getProtocolType(),typeid(*request).name()));
	    std::auto_ptr<ProtoSrv> response(request->execute(client));
	    if(response.get() == 0){
                ACE_DEBUG((LM_DEBUG,"Make Resp Fail(%s)\n",strerror(errno)));
		continue;
	    }

            ACE_DEBUG((LM_DEBUG,"Made Response(%d:%s)\n",response->getProtocolType(),typeid(*response).name()));
	    if(response->execute(client) == (ProtoSrv*)E_FAIL){
                ACE_DEBUG((LM_DEBUG,"Sent Resp Fail(%s)\n",strerror(errno)));
		remove(client);
	    }
	    else
                ACE_DEBUG((LM_DEBUG,"Sent Response OK\n\n"));
	}
    }

    return true;
}

bool TetrixServer::broadcast(std::auto_ptr<SimpleProtoSrv> &msg)
{
    ACE_HANDLE handle;
    ACE_Handle_Set_Iterator iter(_master_set);

    while((handle = iter()) != ACE_INVALID_HANDLE){
        if(handle != _acceptor.get_handle()){
            ACE_SOCK_Stream peer(handle);
            ACE_INET_Addr addr;
            peer.get_remote_addr(addr);
            if(msg->execute(peer) != E_SUCCESS)
                ACE_DEBUG((LM_DEBUG,"Broadcast Fail(%s:%d,%d:%s,reason:%s)\n",addr.get_host_addr(),addr.get_port_number(),msg->getProtocolType(),typeid(*msg).name(),strerror(errno)));
            else
                ACE_DEBUG((LM_DEBUG,"Broadcast OK(%s:%d,%d:%s)\n",addr.get_host_addr(),addr.get_port_number(),msg->getProtocolType(),typeid(*msg).name()));
        }
    }
    return true;
}
